`timescale 1ns / 1ps
`include "defines.vh"
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/06/10 17:26:10
// Design Name: 
// Module Name: ID
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

// ID主要功能：1.分割指令为不同操作码传给ex 2.从寄存器中获取操作数 3. 判断是否有写入操作，若有则计算写入地址 4.计算分支目标地址
module ID (
    input [0:0] clk,
    input [0:0] rst,

    //指令
    input [`Instruction_Bus] Instruction_Input,  //指令内容
    input [`Instruction_Addr_Bus] Instruction_Addr_Input,

    //读取寄存器组值
    output reg[`Reg_Addr_Bus] Read_Reg_1_Addr_Output,  // 读通用寄存器1地址
    output reg[`Reg_Addr_Bus] Read_Reg_2_Addr_Output,  // 读通用寄存器2地址



    //译码结果(传输到ex阶段)
    output reg [`Instruction_Bus] Instruction_Output,  //指令内容
    output reg [`Instruction_Addr_Bus] Instruction_Addr_Output,

    output reg [6:0] opcode,  //操作码
    output reg [11:0] imm12,  //12位立即数
    output reg [20:0] imm20,  //20位立即数
    output reg [0:0] dis,  //区分码
    output reg [2:0] func3,  //func3
    output reg [6:0] func7,  //func7


    output reg [0:0] Write_Reg_Flag,  //寄存器写标志
    output reg [`Reg_Addr_Bus] Write_Reg_Addr  //寄存器写地址



);
    // 不需要传给CU的部分
    reg [`Reg_Addr_Bus] rd;
    reg [`Reg_Addr_Bus] rs1;
    reg [`Reg_Addr_Bus] rs2;










    always @(*) begin
        // 先给所有output赋初始值(阻塞赋值)

        Instruction_Output = Instruction_Input;
        Instruction_Addr_Output = Instruction_Addr_Input;

        opcode = 0;

        //根据risc-v指令结构将指令内容划分

        opcode = Instruction_Input[6:0];
        imm12 = Instruction_Input[31:20];
        imm20 = Instruction_Input[31:12];
        dis = Instruction_Input[30:30];

        func3 = Instruction_Input[14:12];
        func7 = Instruction_Input[31:25];





        rd = Instruction_Input[11:7];
        rs1 = Instruction_Input[19:15];
        rs2 = Instruction_Input[24:20];


        // 按照具体指令赋值，先给默认值



        Write_Reg_Flag = `Write_Reg_Flag_Disabled;
        Write_Reg_Addr = `Regs_Addr_Rst;
        Read_Reg_1_Addr_Output = `Regs_Addr_Rst;
        Read_Reg_2_Addr_Output = `Regs_Addr_Rst;





        //根据具体指令内容执行具体操作
        case (opcode)
            // I型指令
            `INST_TYPE_I: begin
                case (func3)
                    // ADDI 指令
                    `INST_ADDI: begin
                        // 写rd寄存器
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 不使用rs2寄存器，设置为0
                        Read_Reg_2_Addr_Output = `Regs_Addr_Rst;
                    end
                    `INST_SLTI: begin
                        // 写rd寄存器
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                    end
                    `INST_SLTIU: begin
                        // 写rd寄存器
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                    end

                    `INST_XORI: begin
                        // 写rd寄存器
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                    end

                    `INST_ORI: begin
                        // 写rd寄存器
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                    end
                    `INST_ANDI: begin
                        // 写rd寄存器
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                    end
                    `INST_SLLI: begin
                        // 写rd寄存器
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                    end

                    `INST_SRLI_or_SRAI: begin
                        case (func7)
                            `INST_SRLI: begin
                                // 写rd寄存器
                                Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                                Write_Reg_Addr = rd;

                                // 读rs1寄存器
                                Read_Reg_1_Addr_Output = rs1;
                            end


                            `INST_SRAI: begin
                                // 写rd寄存器
                                Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                                Write_Reg_Addr = rd;

                                // 读rs1寄存器
                                Read_Reg_1_Addr_Output = rs1;
                            end


                            default: begin

                            end
                        endcase





                    end




                    default begin
                        // 未识别出输入的指令，设置写入使能为0,地址为0，值为0，读寄存器值为0
                        Write_Reg_Flag = `Write_Reg_Flag_Disabled;
                        Write_Reg_Addr = `Regs_Addr_Rst;

                        Read_Reg_1_Addr_Output = `Regs_Addr_Rst;
                        Read_Reg_2_Addr_Output = `Regs_Addr_Rst;
                    end
                endcase
            end

            `INST_TYPE_R: begin
                case (func3)
                    `INST_ADD_or_SUB: begin
                        case (func7)
                            `INST_ADD: begin
                                Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                                Write_Reg_Addr = rd;
                                // 读rs1寄存器
                                Read_Reg_1_Addr_Output = rs1;
                                // 读rs2寄存器
                                Read_Reg_2_Addr_Output = rs2;

                            end

                            `INST_SUB: begin
                                Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                                Write_Reg_Addr = rd;
                                // 读rs1寄存器
                                Read_Reg_1_Addr_Output = rs1;
                                // 读rs2寄存器
                                Read_Reg_2_Addr_Output = rs2;

                            end





                            default begin
                                // 未识别出输入的指令，设置写入使能为0,地址为0，值为0，读寄存器值为0

                                Read_Reg_1_Addr_Output = `Regs_Addr_Rst;
                                Read_Reg_2_Addr_Output = `Regs_Addr_Rst;
                            end
                        endcase

                    end

                    `INST_SLL: begin
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;

                    end
                    `INST_SLT: begin
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;

                    end
                    `INST_SLTU: begin
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;

                    end

                    `INST_XOR: begin
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;

                    end

                    `INST_SRL_o_SRA: begin
                        case (func7)
                            `INST_SRL: begin
                                Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                                Write_Reg_Addr = rd;
                                // 读rs1寄存器
                                Read_Reg_1_Addr_Output = rs1;
                                // 读rs2寄存器
                                Read_Reg_2_Addr_Output = rs2;

                            end

                            `INST_SRA: begin
                                Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                                Write_Reg_Addr = rd;
                                // 读rs1寄存器
                                Read_Reg_1_Addr_Output = rs1;
                                // 读rs2寄存器
                                Read_Reg_2_Addr_Output = rs2;

                            end
                        endcase
                    end

                    `INST_OR: begin
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;

                    end


                    `INST_AND: begin
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;

                    end



                    default begin
                        // 未识别出输入的指令，设置写入使能为0,地址为0，值为0，读寄存器值为0

                        Read_Reg_1_Addr_Output = `Regs_Addr_Rst;
                        Read_Reg_2_Addr_Output = `Regs_Addr_Rst;
                    end
                endcase
            end

            //S型指令
            `INST_TYPE_S: begin
                case (func3)
                    `INST_SB: begin
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;
                    end
                    `INST_SH: begin
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;
                    end
                    `INST_SW: begin

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;
                    end
                endcase
            end

            `INST_TYPE_L: begin
                case (func3)
                    `INST_LB: begin
                        //读RAM地址 = rs1值+偏移地址       写入寄存器地址 = rd

                        //写rd寄存器
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                    end
                    `INST_LH: begin
                        //读RAM地址 = rs1值+偏移地址       写入寄存器地址 = rd

                        //写rd寄存器
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                    end

                    `INST_LW: begin
                        //读RAM地址 = rs1值+偏移地址       写入寄存器地址 = rd

                        //写rd寄存器
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;

                    end
                    `INST_LBU: begin
                        //读RAM地址 = rs1值+偏移地址       写入寄存器地址 = rd

                        //写rd寄存器
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                    end
                    `INST_LHU: begin
                        //读RAM地址 = rs1值+偏移地址       写入寄存器地址 = rd

                        //写rd寄存器
                        Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                        Write_Reg_Addr = rd;

                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                    end
                    default: begin

                    end
                endcase
            end


            `INST_JAL: begin
                //写rd寄存器
                Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                Write_Reg_Addr = rd;
            end

            `INST_JALR: begin
                //写rd寄存器
                Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                Write_Reg_Addr = rd;
            end



            `INST_TYPE_B: begin
                case (func3)
                    `INST_BEQ: begin
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;
                    end
                    `INST_BNE: begin
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;
                    end
                    `INST_BLT: begin
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;
                    end
                    `INST_BGE: begin
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;
                    end
                    `INST_BLTU: begin
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;
                    end
                    `INST_BGEU: begin
                        // 读rs1寄存器
                        Read_Reg_1_Addr_Output = rs1;
                        // 读rs2寄存器
                        Read_Reg_2_Addr_Output = rs2;
                    end


                endcase
            end


            `INST_LUI: begin
                //写rd寄存器
                Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                Write_Reg_Addr = rd;
            end

            `AUIPC: begin
                //写rd寄存器
                Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                Write_Reg_Addr = rd;
            end

            
            `INST_ECALL:begin
                //读a7寄存器确定操作
                Read_Reg_1_Addr_Output = 5'b10001;
                //读a0寄存器用于输出
                Read_Reg_2_Addr_Output = 5'b01010;


                Write_Reg_Flag = `Write_Reg_Flag_Enabled;
                Write_Reg_Addr = 5'b01010;

            end




            default begin
                // 未识别出输入的指令，设置写入使能为0,地址为0，值为0，读寄存器值为0
                Write_Reg_Flag = `Write_Reg_Flag_Disabled;
                Write_Reg_Addr = `Regs_Addr_Rst;

                Read_Reg_1_Addr_Output = `Regs_Addr_Rst;
                Read_Reg_2_Addr_Output = `Regs_Addr_Rst;
            end
        endcase

    end

    always @(posedge clk) begin
        $display($time, "      ID  in => %d", Instruction_Output);
        $display(
            $time,
            "      ID  out => opcode: %d,func3:%d,Write_able:%d,  Write_addr:%d,  reg1addr:%d,  reg2addr:%d,  imm12:%d,  imm20:%d,  ",
            opcode, func3, Write_Reg_Flag, Write_Reg_Addr,
            Read_Reg_1_Addr_Output, Read_Reg_2_Addr_Output, $signed(imm12), $signed(imm20));
    end



endmodule
